<?php

/**
 * +----------------------------------------------------------------------
 * | 润憬商城系统 [ 高性价比的通用商城系统 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2022~2023 https: *www.honc.fun All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed 这不是一个自由软件，不允许对程序代码以任何形式任何目的的再发行
 * +----------------------------------------------------------------------
 * | Author: 润憬科技 Hon(陈烁临) <2275604210@qq.com>
 * +----------------------------------------------------------------------
 */

namespace cores\traits;

trait RequestTrait
{
  /**
   * 全局过滤规则
   * @var array
   */
  protected $filter;

  /**
   * 获取包含文件在内的请求参数
   * @access public
   * @param  string|array $name 变量名
   * @param  string|array $filter 过滤方法
   * @return mixed
   */
  public function param($name = '', $filter = '')
  {
    $data = array_merge(request()->all(), request()->file() ?: []);
    if (is_array($name)) {
      $data = $this->only($name, $data, $filter);
    } elseif ($name) {
      $data = $data[$name] ?? null;
    }

    return $data;
  }

  /**
   * 获取GET参数
   * @access public
   * @param  string|array $name 变量名
   * @param  mixed        $default 默认值
   * @param  string|array $filter 过滤方法
   * @return mixed
   */
  public function get($name = '', $default = null, $filter = '')
  {
    if (is_array($name)) {
      return $this->only($name, request()->all(), $filter);
    }

    return $this->input(request()->all(), $name, $default, $filter);
  }

  /**
   * 获取POST参数
   * @access public
   * @param  string|array $name 变量名
   * @param  mixed        $default 默认值
   * @param  string|array $filter 过滤方法
   * @return mixed
   */
  public function post($name = '', $default = null, $filter = '')
  {
    if (is_array($name)) {
      return $this->only($name, request()->all(), $filter);
    }

    return $this->input(request()->all(), $name, $default, $filter);
  }

  /**
   * 获取变量 支持过滤和默认值
   * @access public
   * @param  array        $data 数据源
   * @param  string|false $name 字段名
   * @param  mixed        $default 默认值
   * @param  string|array $filter 过滤函数
   * @return mixed
   */
  public function input(array $data = [], $name = '', $default = null, $filter = '')
  {
    if (false === $name) {
      // 获取原始数据
      return $data;
    }

    $name = (string) $name;
    if ('' != $name) {
      // 解析name
      if (strpos($name, '/')) {
        [$name, $type] = explode('/', $name);
      }

      $data = $this->getData($data, $name);

      if (is_null($data)) {
        return $default;
      }

      if (is_object($data)) {
        return $data;
      }
    }

    $data = $this->filterData($data, $filter, $name, $default);

    if (isset($type) && $data !== $default) {
      // 强制类型转换
      $this->typeCast($data, $type);
    }

    return $data;
  }

  protected function filterData($data, $filter, $name, $default)
  {
    // 解析过滤器
    $filter = $this->getFilter($filter, $default);

    if (is_array($data)) {
      array_walk_recursive($data, [$this, 'filterValue'], $filter);
    } else {
      $this->filterValue($data, $name, $filter);
    }

    return $data;
  }

  /**
   * 强制类型转换
   * @access protected
   * @param  mixed  $data
   * @param  string $type
   * @return mixed
   */
  protected function typeCast(&$data, string $type)
  {
    switch (strtolower($type)) {
        // 数组
      case 'a':
        $data = (array) $data;
        break;
        // 数字
      case 'd':
        $data = (int) $data;
        break;
        // 浮点
      case 'f':
        $data = (float) $data;
        break;
        // 布尔
      case 'b':
        $data = (bool) $data;
        break;
        // 字符串
      case 's':
        if (is_scalar($data)) {
          $data = (string) $data;
        } else {
          throw new \InvalidArgumentException('variable type error：' . gettype($data));
        }
        break;
    }
  }

  /**
   * 获取数据
   * @access protected
   * @param  array  $data 数据源
   * @param  string $name 字段名
   * @param  mixed  $default 默认值
   * @return mixed
   */
  protected function getData(array $data, string $name, $default = null)
  {
    foreach (explode('.', $name) as $val) {
      if (isset($data[$val])) {
        $data = $data[$val];
      } else {
        return $default;
      }
    }

    return $data;
  }

  /**
   * 设置或获取当前的过滤规则
   * @access public
   * @param  mixed $filter 过滤规则
   * @return mixed
   */
  public function filter($filter = null)
  {
    if (is_null($filter)) {
      return $this->filter;
    }

    $this->filter = $filter;

    return $this;
  }

  protected function getFilter($filter, $default): array
  {
    if (is_null($filter)) {
      $filter = [];
    } else {
      $filter = $filter ?: $this->filter;
      if (is_string($filter) && false === strpos($filter, '/')) {
        $filter = explode(',', $filter);
      } else {
        $filter = (array) $filter;
      }
    }

    $filter[] = $default;

    return $filter;
  }

  /**
   * 递归过滤给定的值
   * @access public
   * @param  mixed $value 键值
   * @param  mixed $key 键名
   * @param  array $filters 过滤方法+默认值
   * @return mixed
   */
  public function filterValue(&$value, $key, $filters)
  {
    $default = array_pop($filters);

    foreach ($filters as $filter) {
      if (is_callable($filter)) {
        // 调用函数或者方法过滤
        if (is_null($value)) {
          continue;
        }

        $value = call_user_func($filter, $value);
      } elseif (is_scalar($value)) {
        if (is_string($filter) && false !== strpos($filter, '/')) {
          // 正则过滤
          if (!preg_match($filter, $value)) {
            // 匹配不成功返回默认值
            $value = $default;
            break;
          }
        } elseif (!empty($filter)) {
          // filter函数不存在时, 则使用filter_var进行过滤
          // filter为非整形值时, 调用filter_id取得过滤id
          $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
          if (false === $value) {
            $value = $default;
            break;
          }
        }
      }
    }

    return $value;
  }

  /**
   * 是否存在某个请求参数
   * @access public
   * @param  string $name 变量名
   * @param  string $type 变量类型
   * @param  bool   $checkEmpty 是否检测空值
   * @return bool
   */
  public function has(string $name, string $type = 'param', bool $checkEmpty = false): bool
  {
    if (!in_array($type, ['param', 'get', 'post', 'put', 'patch', 'route', 'delete', 'cookie', 'session', 'env', 'request', 'server', 'header', 'file'])) {
      return false;
    }

    $param = empty($this->$type) ? $this->$type() : $this->$type;

    if (is_object($param)) {
      return $param->has($name);
    }

    // 按.拆分成多维数组进行判断
    foreach (explode('.', $name) as $val) {
      if (isset($param[$val])) {
        $param = $param[$val];
      } else {
        return false;
      }
    }

    return ($checkEmpty && '' === $param) ? false : true;
  }
  /**
   * 获取指定的参数
   * @access public
   * @param  array        $name 变量名
   * @param  mixed        $data 数据或者变量类型
   * @param  string|array $filter 过滤方法
   * @return array
   */
  public function only(array $name, $data = 'param', $filter = ''): array
  {
    $data = is_array($data) ? $data : $this->$data();

    $item = [];
    foreach ($name as $key => $val) {

      if (is_int($key)) {
        $default = null;
        $key     = $val;
        if (!key_exists($key, $data)) {
          continue;
        }
      } else {
        $default = $val;
      }

      $item[$key] = $this->filterData($data[$key] ?? $default, $filter, $key, $default);
    }

    return $item;
  }

  /**
   * 排除指定参数获取
   * @access public
   * @param  array  $name 变量名
   * @param  string $type 变量类型
   * @return mixed
   */
  public function except(array $name, string $type = 'param'): array
  {
    $param = $this->$type();

    foreach ($name as $key) {
      if (isset($param[$key])) {
        unset($param[$key]);
      }
    }

    return $param;
  }
}
